package com.zx._09_架构.rpc演变过程.rpc_04;

import com.zx.common.entity.UserRpcDto;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

//  优化1：Stub 是 consumer 端的代理对象，负责网络的连接、请求数据发送、响应接受、数据封装等
//  优化2：进阶为动态代理
//  优化3：将请求的参数，改为需要调用的方法和方法参数，这样可以通过代理对象调用多个方法
public class Stub {

    public static <T> T getProxy(final Class<T> clazz) {
        // 动态代理，需要去执行的代码
        final InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(final Object proxy,
                    final Method method, /*#调用方法的名字#*/
                    final Object[] args  /*#请求的参数#*/) throws Throwable {

                final Socket socket;
                try {
                    socket = new Socket("localhost", 8888);
                    final ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());

                    // 调用方法的名字
                    final String methodName = method.getName();
                    final Class<?>[] parameterTypes = method.getParameterTypes();

                    // 请求的参数修改为需要调用的方法及参数
                    output.writeUTF(methodName);
                    output.writeObject(parameterTypes);
                    output.writeObject(args);
                    output.flush();

                    final DataInputStream input = new DataInputStream(socket.getInputStream());
                    final long id = input.readLong();
                    final String name = input.readUTF();
                    final UserRpcDto userRpcDto = new UserRpcDto(id, name);
                    socket.close();
                    return userRpcDto;
                } catch (final IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        };

        final Object object = Proxy.newProxyInstance(clazz.getClassLoader(),
                new Class[]{clazz}, h);

        return (T) object;
    }

}
